/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_SERVERFACE__HEADER_FILE__
#define __COID_SERVER_SERVERFACE__HEADER_FILE__

#include "tree/ttreeid.h"
#include "tree/nodeclass.h"
#include "coid/comm/hptimer.h"
#include "coid/comm/local.h"
#include "coid/comm/dynarray.h"
#include "coid/comm/password.h"
#include "coid/comm/sync/guard.h"
#include "coid/comm/sync/mutex_reg.h"

#include "coid/svc/instance.h"
#include "coid/coid.h"

#include "filter.h"
#include "logcoid.h"
#include "threadmgr.h"

namespace coid {

typedef ClassRegister<ttree_ifc>::NodeClass             NodeClass;


////////////////////////////////////////////////////////////////////////////////
struct CoidNode
{
    virtual ~CoidNode();

    CoidNode()
    {
        _timetodie = 0;
        _id = UMAX32;
#ifdef _DEBUG
        _flags = xVERBOSITY;
#else
        _flags = 1;
#endif

        _mutex.init(0);
		_mutex.set_name( "CoidNode" );
    }

    CoidNode( uint id );


    IMPLEMENTS_VIRTUAL_INTERFACE_ROOT_ttree_ifc_BEGIN_PURE(CoidNode,"0.0");
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

    typedef ClassRegister<ttree_ifc>::NodeClass    NodeClass;

    virtual NodeClass GET_NODECLASS()               { return GET_VIRTUAL_INTERFACE(); }

    virtual token short_name() const;
    virtual token class_name() const                { return GET_VIRTUAL_INTERFACE().get_class_name(); }
    virtual const version& get_version() const      { return GET_VIRTUAL_INTERFACE().get_version(); }
    token object_name() const;
    opcd object_name( const token& n );

    virtual opcd initialize()                       { return 0; }
    virtual uint get_method_id( const token& name ) { return UMAX32; }
    virtual opcd dispatch( uint method, binstream& bin, metastream* meta )
    {
        if( method == StdDispatchMethod::COMMAND )
        {
            charstr txt;
            bin >> txt >> BINSTREAM_ACK;
            token rem(txt);
            token tc = rem.cut_left(' ');
            
            charstr crem(rem);
            command_tokens cts( crem );

			txtstream bint(bin);
            return dispatch_cmd( tc, cts, bint );
        }
        else if( method == StdDispatchMethod::ECHO )
        {
            txtstream txs(bin);
            txs << "#" << _id << "\t" << class_name() << " (" << object_name() << ")\n";
            return 0;
        }
        bin >> BINSTREAM_ACK_EAT;
        return ersUNKNOWN_CMD;
    }

    virtual opcd dispatch_cmd( const token& cmd, command_tokens& arg, txtstream& reply );
    virtual opcd list_cmds( const token& cmd, dynarray<charstr>& cmds ) const;

    opcd stream_cmds_list( const token& cmd, txtstream& reply ) const
    {
        dynarray<charstr> cmds;
        list_cmds( cmd, cmds );
        for( uint i=0; i<cmds.size(); ++i )
            reply << cmds[i] << "\n";
        return 0;
    }

    //opcd dpx_predestroy();
    opcd dpx_destroy();
    opcd dpx_spawn( bool crit );

    static void* __spawn( void* x );

    opcd dpx_authentify( binstream& arg, metastream* meta );
    opcd dpx_authentify_shared( binstream& arg, metastream* meta );
    opcd dpx_authentify_auto();

    opcd dpx_create_interface( const CreateInterfaceInfo& cinfo, binstream& ret );

    opcd dpx_dispatch_loop( netSocket* ns, uint port );

    opcd dpx_command( const charstr& cmd, binstream& rply );
    opcd dpx_echo( binstream& rply );


    bool can_attach() const             { return 0 == (_flags & fREJECT_ATTACH); }
    void set_can_attach( bool bcan )    { if(!bcan) _flags |= fREJECT_ATTACH; else _flags &= ~fREJECT_ATTACH; }

    bool is_exit_signalled() const      { return 0 != (_flags & fSIGNAL_EXIT); }
    void signal_exit()                  { _flags |= fSIGNAL_EXIT; }

    virtual void on_exit_signalled()    {}

    const charstr& name() const         { return _name; }

    opcd threads_free();
    bool threads_running() const
    {
        //if( _thr_own.size()>0 )  return true;
        if( !_thr_using.size() )  return false;
        uints n=_thr_using.size(), a=0;
        for( uints i=0; i<n; ++i )
        {
            if( !thread::exists( _thr_using[i] ) )
                ++a;
        }
        return n>a;
    }

    static void thread_free( thread_t tid );


    ints thread_unregister( uint tid )
    {
        ints pos = _thr_own.contains_back(tid);
        if( pos>=0 )
            _thr_own.del(pos);
        return pos;
    }

    void request_detach();


    //dynarray<logstream_coid*>  _log;        ///< log streams open
/*
    opcd unregister_thread( uint tid )
    {
        GUARDME;

        _thr_own.contains()

        uint p = dynarray_lower_bound( _threads, tid, thrp );
        if( p < _threads.size()  &&  (uint)_threads[p]->_tid == tid )
        {
            thread_info* pt = _threads[p];
            _threads.del(p);
            delete pt;
        }
        else
            return ersNOT_FOUND;

        return 0;
    }
*/
    ////////////////////////////////////////////////////////////////////////////////

    void mutex_wrlock();
    void mutex_rdlock() const;
    void mutex_unlock() const;

    //LOG
    bool verbosity_level_ok( uchar verb )        const  { return verb <= (_flags & xVERBOSITY); }
    uchar get_verbosity() const                         { return _flags & xVERBOSITY; }
    void set_verbosity( uchar verb )                    { _flags += verb - (_flags&xVERBOSITY); }

    binstream& set_msg( uchar msgclass, uchar verb ) const;

    binstream& post_alert(uchar verbose)         const  { return set_msg( LOGMSG::TYPE_ALERT, verbose); }
    binstream& post_critical(uchar verbose)      const  { return set_msg( LOGMSG::TYPE_CRIT, verbose); }
    binstream& post_error(uchar verbose)         const  { return set_msg( LOGMSG::TYPE_ERROR, verbose); }
    binstream& post_warning(uchar verbose)       const  { return set_msg( LOGMSG::TYPE_WARNING, verbose); }
    binstream& post_info(uchar verbose)          const  { return set_msg( LOGMSG::TYPE_INFO, verbose); }
    //connection related
    binstream& post_open()                       const  { return set_msg( LOGMSG::TYPE_OPEN, 0); }
    binstream& post_close()                      const  { return set_msg( LOGMSG::TYPE_CLOSE, 0); }
    binstream& post_term()                       const  { return set_msg( LOGMSG::TYPE_TERM, 0); }
    //server object related
    binstream& post_attach(uchar verbose)        const  { return set_msg( LOGMSG::TYPE_ATTACH, verbose); }
    binstream& post_detach(uchar verbose)        const  { return set_msg( LOGMSG::TYPE_DETACH, verbose); }
    //debug
    binstream& post_debug(uchar verbose)         const  { return set_msg( LOGMSG::TYPE_DEBUG, verbose); }
    binstream& post_trace(uchar verbose)         const  { return set_msg( LOGMSG::TYPE_TRACE, verbose); }

    binstream& post_method()                     const  { return set_msg( LOGMSG::TYPE_METHOD, 3); }
    binstream& post_console_msg()                const  { return set_msg( LOGMSG::TYPE_MESSAGE, 0); }

    uint get_id() const;
    uint get_connection_id() const;

    opcd get_name( charstr& name ) const;
    opcd set_name( const token& name );

    opcd set_option( uint optid, int value );
    opcd get_option( uint optid, int* value ) const;

    opcd read_log( uint& ref, uint max, dynarray<logstream>& list ) const;
    opcd add_filter( const FILTER_ENTRY& flt );
    opcd delete_filter( uint obj, uint fltn );
    opcd get_filters( uint obj, uint fltn, dynarray<FILTER_ENTRY>& out ) const;
    opcd get_filter_ids( uint obj, uint fltn, dynarray<uint>& out ) const;
    opcd get_log_upd( uint* ref ) const;

    opcd set_method_flags( uint obj, uint method, uint flags );

    //opcd attach_service( const token& service, const version& ver, LNID& oid, binstream* arg );

    opcd dispatch_method( uint obj, const token& method, binstream& parinout, metastream* meta );

    opcd spawn_thread( t_thread_fnc start, void* arg, bool crit, const token& name );
    opcd free_threads();

    opcd parse_path( uint id, const token& tok, dynarray<uint>& out ) const;
    charstr& get_server_address( charstr& buf ) const;

    uint get_superior_id() const;
    opcd echo( uint id, txtstream& s ) const;

    opcd list_devices( const token& classname, const version& ver, const token& name, dynarray<uint>& devices ) const;
    opcd list_conn_devices( const token& classname, const version& ver, const token& name, dynarray<uint>& devices ) const;

    opcd get_device_info( uint id, charstr& service, version& ver, charstr& name ) const;

    charstr& get_device_info_string( charstr& buf, uint id=UMAX32 )
    {
        charstr name,v;
        version ver;

        opcd e = get_device_info( id, buf, ver, name );
        if(e)
            buf << opcd_formatter(e);
        else
            buf << "-" << ver.get_version(v) << ":" << name;
        return buf;
    }

    charstr& get_working_dir( charstr& buf ) const;

    opcd find_nodes_of_class( uint from, const charstr& name, const token& service, const version& ver, dynarray<uint>& lst ) const;

    opcd get_conn_info( charstr& out ) const;

    opcd get_object_address( netaddr& addr, uint* obj ) const;

    netAddress* get_server_address_nl( netAddress* buf ) const;

    opcd get_tree_info( dynarray<tree_info>& out, uint* ref ) const;
    opcd get_tree_info_changes( uint& ref, dynarray<tree_info>& out, dynarray<uint>& remove ) const;

    opcd get_method_desc( uint id, const ServiceDescriptor::MethodDesc** pdesc, uint* nmeth ) const;

    opcd set_identity( account_id& acid, bool create );

	opcd open_service( COID_OPEN_SERVICE & cos );

    opcd request_autodestroy( uint secfwd );


#ifdef _DEBUG
	opcd create_list( charstr& out, time_t time=0, bool all=false ) const;
#endif



protected:
    opcd get_threads_status (charstr& out);

#ifdef _DEBUG
    static void create_list( MX_REGISTER_ITERATOR & it, const dynarray<thread> * threads,
                             dynarray<const comm_mutex *> & mxlist, time_t time=0 );
	static void format_list( const dynarray<const comm_mutex *> & mxlist, charstr& out, uints owned );
#endif


public:
    ///Node access guard
    struct GUARD
    {
        typedef comm_mutex_reg REFMX;

        explicit GUARD (CoidNode& n) : _mux(n._mutex)                   { _mux.wr_lock(); }
        explicit GUARD (const CoidNode& n) : _mux((REFMX&)n._mutex)     { _mux.rd_lock(); }
        explicit GUARD (CoidNode* n) : _mux(n->_mutex)                  { _mux.wr_lock(); }
        explicit GUARD (const CoidNode* n) : _mux((REFMX&)n->_mutex)    { _mux.rd_lock(); }

        GUARD( const CoidNode& n, bool isconst ) : _mux((REFMX&)n._mutex)
        {
            if(isconst)
                _mux.rd_lock();
            else
                _mux.wr_lock();
        }

        GUARD( const CoidNode* n, bool isconst ) : _mux((REFMX&)n->_mutex)
        {
            if(isconst)
                _mux.rd_lock();
            else
                _mux.wr_lock();
        }

        ~GUARD() { _mux.unlock(); }

    private:
        REFMX& _mux;
    };


    mutable comm_mutex_reg  _mutex;             ///< synchro mutex
    charstr        _name;
    dynarray<thread> _thr_own;                  ///< threads run by the object
    dynarray<thread> _thr_using;                ///< threads using the object

    uint           _id;                         ///< object id
    charstr        _path;
    uint           _flags;

    uint64         _timetodie;                  ///< time when object requested itself to be terminated

    enum {
        xVERBOSITY                      = 0xff,

        fREJECT_ATTACH                  = 0x00010000,   ///< reject attachs to this object
        fSIGNAL_EXIT                    = 0x00020000,   ///< signal exit flag for threads
    };

};






} // namespace coid

#endif //__COID_SERVER_SERVERFACE__HEADER_FILE__
